package sw6.admin.gui;

import java.util.ArrayList;

import sw6.admin.R;
import sw6.admin.database.DatabaseHelper;
import sw6.admin.gui.logic.GuiBuilder;
import sw6.admin.gui.logic.Menu;
import sw6.admin.gui.logic.Serializer;
import sw6.admin.gui.logic.Setting;
import sw6.admin.internal.Definitions;
import sw6.lib.Settings;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.ListActivity;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.CompoundButton.OnCheckedChangeListener;

/**
 * This activity displyes a list of settings and sub-menues for a given menu. The list of settings is sent to
 * the activity as a paramenter using the Bundle class. When ever the user clicks on a setting,
 * a dialog is show, giving the user the ability to change the value of the setting.
 * If the user clicks on a sub-menu, a new MenuActivity is started, showing the settings and
 * sub-menues for that sub-menu.
 * @author sw6b
 *
 */
public class MenuActivity extends ListActivity {

	private MenuAdapter mAdapter;
	ArrayList<MenuEntry> menueEntries;
	Menu mActiveMenu;

	/**
	 * Reads the Menu instance sent to MenuActivity as a bundle parameter.
	 */
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.menu);
		menueEntries = new ArrayList<MenuEntry>();
		mAdapter = new MenuAdapter(this, R.layout.menu_entry, menueEntries);
		setListAdapter(mAdapter);

		// Reads the Menu instance that was passed to the activity
		Bundle extra = getIntent().getExtras();
		byte[] bytes = extra.getByteArray("menuBytes");
		Menu menu = (Menu)Serializer.deSerializeObject(bytes, Menu.class);

		this.setTitle(menu.getRealName());

		// Populates the ListActivity with settings and sub-menues
		loadMenu(menu);
	}

	/** This method is needed so that when showDialog(int id) is called, android knows what dialog to show.
	 * 	This is the correct "Android" way of opening dialogs
	 */
	protected Dialog onCreateDialog(int id) {
		if (id == -1) {
			try {
				PackageManager pm = this.getPackageManager();
				String label = pm.getApplicationInfo(mActiveMenu.getPackageName(), 0).loadLabel(pm).toString();
				return getErrorDialog("An error occured while trying to open this setting. The error was caused by a fault in the \"" + label + "\" application.");
			} catch (NameNotFoundException e) {
				return getErrorDialog("An error occured while trying to open this setting.");
			}
		}
		Dialog dialog = mActiveMenu.getSettings().get(id).getDialog();
		return dialog;
	}

	/**
	 * Calls GuiBuilder to build the gui dialogs for the menu, and create a list MenuEntries.
	 * The menu entries are used to create the list of settings in the ListActivity
	 * @param menu
	 */
	private void loadMenu(Menu menu) {		
		if (mActiveMenu != null) {
			mAdapter.clear();
		}

		mActiveMenu = menu;
		// Creates the MenuEntries for the settings in the current menu.
		new GuiBuilder().buildDialogs(menu, MenuActivity.this);
		ArrayList<MenuEntry> menuEntries = BuildMenuEntryList(menu);
		// Populates the ListActivity with the MenuActivities
		for(MenuEntry me: menuEntries) {
			mAdapter.add(me);
		}
	}

	public ArrayList<MenuEntry> BuildMenuEntryList(Menu menu) {
		ArrayList<MenuEntry> menuEntries = new ArrayList<MenuEntry>();

		for (Setting setting : menu.getSettings()) {

			boolean isCheckBox = false;
			if (setting.isBoolean())
				isCheckBox = true;

			MenuEntry me = new MenuEntry(setting.getRealName(), setting.getDescription(), isCheckBox, menu.getPackageName(), setting.getVarName());
			menuEntries.add(me);
		}

		for (Menu menuItem : menu.getMenus()) {
			menuEntries.add(new MenuEntry(menuItem.getRealName(), menuItem.getDescription(), false, "", ""));
		}

		return menuEntries;
	}

	/**
	 * If a setting of sub-menu is clicked by the user, this method is called.
	 * If the item clicked is a sub-menu, a new MenuActivity is loaded
	 * If a checkbox was clicked, the value of the checkbox is inverted
	 * if a setting was clicked, the dialog for the setting is started
	 */
	@Override
	protected void onListItemClick(ListView l, View v, int position, long id) {

		// If this is true, the user clicked on a sub-menu.
		if (position > mActiveMenu.getSettings().size()-1) {
			// Loads the sub-menu that the user clicked on, and opens a new activity
			int menuPos = position - mActiveMenu.getSettings().size();
			Menu menu = mActiveMenu.getMenus().get(menuPos);

			byte[] bytes = Serializer.serializeObject(menu);
			Intent intent = new Intent(this, MenuActivity.class);

			intent.putExtra("menuBytes", bytes);
			startActivity(intent);
		}
		else {
			Setting setting = mActiveMenu.getSettings().get(position);

			// If the setting is a boolean
			if (setting.isBoolean() == true)
			{
				CheckBox cb = (CheckBox)v.findViewById(R.id.checkBox1);
				// Invertes the checkbox value
				cb.setChecked(!cb.isChecked());
			}
			// If the setting is a custom setting
			else if (setting.getDataType().equals(Definitions.XML_SETTING_TYPE_OBJECT)) {

				// If the activity is pointing to the android settings, we have to launch the activity in a special way
				if (setting.getActivity().equals("android.settings.SETTINGS")) {
					startActivity(new Intent(setting.getActivity()));
				}
				// Else it is just a normal custom setting
				else {
					// Starts the external activiy that that will help the user modify the value
					// of the custom setting. The varName of the setting is passed on to the
					// activity as a bundle parameter

					if (validActivity(mActiveMenu.getPackageName(), setting.getActivity())) {
						Intent intent = new Intent();
						intent.setClassName(mActiveMenu.getPackageName(), setting.getActivity());

						fillIntentWithSetting(intent, setting, mActiveMenu.getPackageName());

						startActivity(intent);						
					}
					else {
						showDialog(-1);
					}
				}
			}
			else if  (setting.getDataType().equals(Definitions.XML_SETTING_TYPE_STDOBJECT)) {

				String activityPath = Definitions.getStdObjectActivity(setting.getTypeName());

				Intent intent = new Intent();
				intent.setClassName(Definitions.SW6_ADMIN_PACKAGE_NAME, activityPath);

				fillIntentWithSetting(intent, setting, mActiveMenu.getPackageName());

				startActivity(intent);
			}

			// Else, the setting is an integer, double or string
			else
				showDialog(position);
		}

		super.onListItemClick(l, v, position, id);
	}

	private Dialog getErrorDialog(String error) {
		AlertDialog.Builder builder = new AlertDialog.Builder(this);
		builder.setMessage(error)
		.setCancelable(false)
		.setPositiveButton("OK", new DialogInterface.OnClickListener() {
			public void onClick(DialogInterface dialog, int id) {
				// put your code here
			}
		});
		AlertDialog alertDialog = builder.create();
		alertDialog.show();

		return alertDialog;
	}

	private boolean validActivity(String packageName, String activity) {
		PackageManager pm = this.getPackageManager();
		try {
			for (ActivityInfo ai : pm.getPackageInfo(packageName, PackageManager.GET_ACTIVITIES).activities) {
				if (ai.name.equals(activity) && ai.exported) {
					return true;
				}
			}
		} catch (NameNotFoundException e) { }
		return false;
	}

	private void fillIntentWithSetting(Intent intent, Setting setting, String packageName) {
		intent.putExtra(Settings.OBJECT_ATTRIBUTE_VARNAME, setting.getVarName());
		intent.putExtra(Settings.OBJECT_ATTRIBUTE_DATATYPE, setting.getDataType());
		intent.putExtra(Settings.OBJECT_ATTRIBUTE_DESCRIPTION, setting.getDescription());
		intent.putExtra(Settings.OBJECT_ATTRIBUTE_REALNAME, setting.getRealName());
		intent.putExtra(Settings.OBJECT_ATTRIBUTE_PACKAGENAME, packageName);
	}

	private class MenuAdapter extends ArrayAdapter<MenuEntry> {

		private ArrayList<MenuEntry> items;

		public MenuAdapter(Context context, int textViewResourceId, ArrayList<MenuEntry> items) {
			super(context, textViewResourceId, items);
			this.items = items;
		}

		// This method populates the elements in the ListActivity with information form the MenuEntries
		@Override
		public View getView(int position, View convertView, ViewGroup parent) {
			View v = convertView;
			LayoutInflater vi = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
			v = vi.inflate(R.layout.menu_entry, null);

			final MenuEntry o = items.get(position);
			TextView tt = (TextView) v.findViewById(R.id.toptext);
			TextView bt = (TextView) v.findViewById(R.id.bottomtext);
			CheckBox ch = (CheckBox) v.findViewById(R.id.checkBox1);

			// Sets the title and description of the setting
			tt.setText(o.getRealName());
			bt.setText(o.getShortDesc());

			// If the MenuEntry is not a boolean, then I hide the checkbox in the gui
			if (o.getIsCheckbox() == false)
			{
				ch.setVisibility(View.GONE);
			}
			else
			{
				// If the MenuEntry is a boolean, I load the boolean value from the database and sets the checkbox value with it
				CheckBox checkBox = (CheckBox) v.findViewById(R.id.checkBox1);
				
				DatabaseHelper dh = new DatabaseHelper(getContext());
				
				int value = dh.getBoolean(o.getPackageName(), o.getVarName()).getInt(0);
				boolean checked;
				if (value == 0)
					checked = false;
					else
				checked = true;
				
				dh.close();
				
				checkBox.setChecked(checked);

				// I set up a listener, so that if the checkbox value changes, the change is saved to the database
				checkBox.setOnCheckedChangeListener(new OnCheckedChangeListener() {

					@Override
					public void onCheckedChanged(CompoundButton buttonView,
							boolean isChecked) {
						DatabaseHelper db = new DatabaseHelper(getContext());
						db.updateBoolean(o.getPackageName(), o.getVarName(), isChecked);
						db.close();
					}});
			}

			return v;
		}
	}
}
